#include <8051.h>
#include "ser_ir.h"

#define TXBUFLEN 3
#define RXBUFLEN 18      // The minimum rx buffer size for safe communications
// is 17. (The UART in the PC has a 16-byte FIFO.)
// TXBUFLEN & RXBUFLEN can be highher if rxbuf[] and txbuf[] is in xdata, max size is limited to 256!

#define THRESHOLD 16
#define ENABLE    0
#define DISABLE   1

#define CTS P3_6          // CTS & RTS can be assigned to any free pins
#define RTS P3_7

// You might want to specify idata, pdata or xdata for the buffers
static unsigned char rxbuf[RXBUFLEN], txbuf[TXBUFLEN];
static unsigned char rxcnt, txcnt, rxpos, txpos;
static bit busy;

void
ser_init() {
  ES = 0;
  rxcnt = txcnt = rxpos = txpos = 0;  // init buffers
  busy = 0;
  SCON = 0x50;               // mode 1 - 8-bit UART
  PCON |= 0x80;              // SMOD = 1;
  TMOD &= 0x0f;              // use timer 1
  TMOD |= 0x20;
  //  TL1 = TH1 = 256 - 104;      // 600bps with 12 MHz crystal
  //  TL1 = TH1 = 256 - 52;      // 1200bps with 12 MHz crystal
  //  TL1 = TH1 = 256 - 26;      // 2400bps with 12 MHz crystal
  TL1 = TH1 = 256 - 13;      // 4800bps with 12 MHz crystal
  TR1 = 1;                   // Enable timer 1
  ES = 1;
  CTS = ENABLE;
}

void
ser_handler( void ) interrupt 4 {
  if( RI ) {
    RI = 0;
    /* don't overwrite chars already in buffer */
    if( rxcnt < RXBUFLEN ) {
      rxbuf [( unsigned char )( rxpos + rxcnt++ ) % RXBUFLEN] = SBUF;
    }
    if( rxcnt >= ( RXBUFLEN - THRESHOLD ) ) {
      CTS = DISABLE;
    }
  }
  if( TI ) {
    TI = 0;
    if( busy = txcnt ) {  /* Assignment, _not_ comparison! */
      txcnt--;
      SBUF = txbuf[txpos++];
      if( txpos >= TXBUFLEN ) {
        txpos = 0;
      }
    }
  }
}

void
ser_putc( unsigned char c ) {
  while( txcnt >= TXBUFLEN ); // wait for room in buffer
  while( RTS == DISABLE );
  ES = 0;
  if( busy ) {
    txbuf[( unsigned char )( txpos + txcnt++ ) % TXBUFLEN] = c;
  } else {
    SBUF = c;
    busy = 1;
  }
  ES = 1;
}

unsigned char
ser_getc( void ) {
  unsigned char c;
  while( !rxcnt ) {       // wait for a character
    CTS = ENABLE;
  }
  ES = 0;
  rxcnt--;
  c = rxbuf[rxpos++];
  if( rxpos >= RXBUFLEN ) {
    rxpos = 0;
  }
  ES = 1;
  return ( c );
}

#pragma save
#pragma noinduction
void
ser_puts( unsigned char *s ) {
  unsigned char c;
  while( c = *s++ ) {
    if( c == '\n' ) {
      ser_putc( '\r' );
    }
    ser_putc( c );
  }
}
#pragma restore

void
ser_gets( unsigned char *s, unsigned char len ) {
  unsigned char pos, c;
  pos = 0;
  while( pos <= len ) {
    c = ser_getc();
    if( c == '\r' ) {
      continue;
    }        // discard CR's
    s[pos++] = c;
    if( c == '\n' ) {
      break;
    }           // NL terminates
  }
  s[pos] = '\0';                  // terminate string
}

unsigned char
ser_can_xmt( void ) {
  return TXBUFLEN - txcnt;
}

unsigned char
ser_can_rcv( void ) {
  return rxcnt;
}
